package com.xz.purchase.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xz.apply.service.IPurchaseApplyService;
import com.xz.apply.service.IPurchaseProductApplyService;
import com.xz.common.core.domain.AjaxResult;
import com.xz.common.utils.DateUtils;
import com.xz.common.utils.RedisCode;
import com.xz.common.utils.SecurityUtils;
import com.xz.common.utils.StringUtils;
import com.xz.log.service.IOperationLogService;
import com.xz.process.service.IProcessOrderItemService;
import com.xz.process.service.IProcessOrderService;
import com.xz.product.domain.Product;
import com.xz.product.dto.AttributeParamDto;
import com.xz.product.dto.ProductDto;
import com.xz.product.service.IProductService;
import com.xz.purchase.domain.Purchase;
import com.xz.purchase.domain.PurchasePrice;
import com.xz.purchase.domain.PurchaseProduct;
import com.xz.purchase.dto.PurchaseProductImportDto;
import com.xz.purchase.dto.PurchaseResourceDto;
import com.xz.purchase.mapper.PurchaseMapper;
import com.xz.purchase.mapper.PurchasePriceMapper;
import com.xz.purchase.service.IPurchaseImportService;
import com.xz.purchase.service.IPurchasePriceService;
import com.xz.purchase.service.IPurchaseProductService;
import com.xz.purchase.service.IPurchaseService;
import com.xz.purchase.utils.PurchaseImportUtil;
import com.xz.purchaseOrder.service.IPurchaseOrderService;
import com.xz.purchaseOrder.service.IPurchaseProductOrderService;
import com.xz.repertory.service.IRepertoryFlowService;
import com.xz.sales.mapper.SalesPurchaseMapper;
import com.xz.sales.service.ISalesOrderDetailService;
import com.xz.supplier.domain.Supplier;
import com.xz.supplier.service.ISupplierService;
import com.xz.warehouse.domain.Warehouse;
import com.xz.warehouse.service.IWarehouseService;
import common.RRException;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 采购Service业务层处理
 *
 * @author xz
 * @date 2024-01-06
 */
@Service
public class PurchaseImportServiceImpl implements IPurchaseImportService {

	@Autowired
	private IPurchaseService purchaseService;
	@Autowired
	private IPurchasePriceService purchasePriceService;
	@Autowired
	private IPurchaseProductService iPurchaseProductService;
	@Autowired
	private IRepertoryFlowService iRepertoryFlowService;
	@Autowired
	private IWarehouseService iWarehouseService;
	@Autowired
	private IProductService iProductService;
	@Autowired
	private ISupplierService iSupplierService;
	@Autowired
	private IProductService productService;

	/**
	 * 导入库存商品
	 *
	 * @param importBatchNo 此次系统批量入库的批次号
	 * @param importDtos    导入的数据
	 * @return 导入失败的数据
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public List<PurchaseProductImportDto> importExcel(String importBatchNo,
	                                                  List<PurchaseProductImportDto> importDtos) {

		if (CollectionUtils.isEmpty(importDtos)) {
			throw new RRException("导入数量不能为空");
		}
		// 移除空数据
		importDtos.removeIf(a -> null == a);
		if (importDtos.size() > 200) {
			throw new RRException("请勿导入超过200条商品数据");
		}
		// 用于收集导入失败的数据集合
		List<PurchaseProductImportDto> importFailedDto = new ArrayList<>();
		// 转换成（采购入库）商品，并且把转换失败的商品放入到失败的数据集合中
		List<PurchaseProduct> purchaseProducts = toProducts(importBatchNo, importDtos, importFailedDto);
		// 清除数据,释放
		// importDtos.clear();
		// 保存采购单数据
		//savePurchaseProduct(purchaseProducts);
		// 返回失败的数据，如果失败数据为0，说明全部导入成功
		//return importFailedDto;

		// 变更，现在是单元操作，要不全部成功，要不全部失败
		if (CollectionUtils.isEmpty(importFailedDto)) {
			// 保存采购单数据
			savePurchaseProduct(purchaseProducts);
			return importFailedDto;//空的importFailedDto
		} else {
			// 只要有一笔错误，就返回所有的数据，也就是importDtos本身。错误信息字段在里面赋值好了。
			return importDtos;
		}
	}

	/**
	 * 导入数据转换（采购入库）商品
	 *
	 * @param importBatchNo   此次系统批量入库的批次号
	 * @param importDtos      导入的数据
	 * @param importFailedDto 导入失败的数据
	 * @return 采购商品数据
	 */
	private List<PurchaseProduct> toProducts(String importBatchNo, List<PurchaseProductImportDto> importDtos,
	                                         List<PurchaseProductImportDto> importFailedDto) {


		// 获取数据库中与采购导入关联的数据（如供应商，仓库，商品），并且转换成以名称为key的map
		PurchaseResourceDto resource = getResoure();
		// 开始构建（采购入库）商品
		List<PurchaseProduct> purchaseProducts = new ArrayList<>();
		importDtos.forEach(importDto -> {
			// 初始化采购商品
			PurchaseProduct purchaseProduct = initPurchaseProduct(importBatchNo, importDto, resource);
			// 如果初始化失败，isFail()会返回true，这时需要放入到失败的集合中。这些失败数据以待后用。
			if (importDto.isFail()) {
				importFailedDto.add(importDto);
			} else {
				purchaseProducts.add(purchaseProduct);
			}
		});
		return purchaseProducts;
	}

	/**
	 * 构建采购商品
	 *
	 * @param importBatchNo 此次系统批量入库的批次号
	 * @param importDto     导入的数据
	 * @param resource      关联的数据
	 */
	private PurchaseProduct initPurchaseProduct(String importBatchNo, PurchaseProductImportDto importDto,
	                                            PurchaseResourceDto resource) {

		StringBuilder failInfo = new StringBuilder(); // 错误信息
		PurchaseProduct purchaseProduct = new PurchaseProduct(); // 返回结果（采购商品）

		// 开始填充基本信息
		purchaseProduct.setImportBatchNo(importBatchNo);// 此次系统批量入库的批次号
		purchaseProduct.setCreateTime(DateUtils.getNowDate());
		purchaseProduct.setCreateBy(SecurityUtils.getUserId());
		purchaseProduct.setTenantId(SecurityUtils.getLoginUser().getTenantId());
		purchaseProduct.setBatchDate(importDto.getBatchDate());
		purchaseProduct.setValidity(importDto.getValidity());
		purchaseProduct.setStatus(1);

		// 开始填充导入的字段
		purchaseProduct.setBatchNumber(importDto.getBatchNumber());
		purchaseProduct.setDeliveryNumber(importDto.getDeliveryNumber());
		purchaseProduct.setRemark(importDto.getRemark());

		// 处理入库数量字段
		if (null == importDto.getStorageNum() || importDto.getStorageNum().intValue() < 1) {
			failInfo.append("商品数量不能为空，并且须大于0;\n");
		}
		purchaseProduct.setStorageNum(importDto.getStorageNum());
		purchaseProduct.setAvailableStock(importDto.getStorageNum());
		purchaseProduct.setInventoryQuantity(importDto.getStorageNum());// 设置这个值是因为，后续调用公共新增库存流水记录时要用到的

		// 获取和填充供应商字段
		Supplier supplier = resource.getSupplier(importDto.getSupplierName());
		if (null != supplier) {
			purchaseProduct.setSupplierId(supplier.getId());
			purchaseProduct.setSupplierName(supplier.getUnitName());
		} else if (StringUtils.isNotEmpty(importDto.getSupplierName())) {
			failInfo.append(String.format("供应商[%s]不存在;\n", importDto.getSupplierName()));
		} else {
			failInfo.append("供应商不能为空;\n");
		}

		// 获取和填充仓库字段
		Warehouse warehouse = resource.getWarehouse(importDto.getWarehouseName());
		if (null != warehouse) {
			purchaseProduct.setWarehouseId(warehouse.getId());
			purchaseProduct.setWarehouseName(warehouse.getWarehouseName());
			purchaseProduct.setDeptId(SecurityUtils.getDeptId());// 采购商品，采购单的部门id与仓库一致
		} else if (StringUtils.isNotEmpty(importDto.getWarehouseName())) {
			failInfo.append(String.format("仓库[%s]不存在;\n", importDto.getWarehouseName()));
		} else {
			failInfo.append("仓库不能为空;\n");
		}

		// 获取和填充商品字段
		ProductDto productDto = resource.getProduct(importDto.getProductName());
		if (null != productDto) {
			purchaseProduct.setProductId(productDto.getId());
			purchaseProduct.setProductName(productDto.getProductName());
			purchaseProduct.setProductEncoded(productDto.getEncoded());
			purchaseProduct.setProductCategory(productDto.getProductCategory());
			purchaseProduct.setBrandName(productDto.getBrandName());
			purchaseProduct.setProdName(productDto.getProdName());
			purchaseProduct.setUnit(productDto.getUnitName());
			purchaseProduct.setManufacturer(productDto.getManufacturer());
			purchaseProduct.setRegistrationNumber(productDto.getRegistrationNo());

			// 如果商品是医疗器械，则导入时需要填写生产日期，有效期
			if (null != productDto.getIsDevices() && 1 == productDto.getIsDevices().intValue()) {
				if (null == importDto.getBatchDate()) {
					failInfo.append("该商品为医疗器械生产日期必填;\n");
				}

				if (null == importDto.getValidity()) {
					failInfo.append("该商品为医疗器械有效期必填;\n");
				}
			}
		} else if (StringUtils.isNotEmpty(importDto.getProductName())) {
			failInfo.append(String.format("商品[%s]不存在;\n", importDto.getProductName()));
		} else {
			failInfo.append("商品不能为空;\n");
		}

		// 商品id不为null的情况下，需要设置和校验参数字段
		final Long productId = purchaseProduct.getProductId();
		if (null != productId) {
			// 获取参数信息,这个方法不会返回null的数据，但可能返回size为0的集合
			// 返回size为0的集合说明这个商品不需要参数
			List<AttributeParamDto> paramList = getProductParams(resource, productId);
			// 设置purchaseProduct对象的ProductParam字段值
			PurchaseImportUtil.setProductParam(importDto, purchaseProduct, paramList, failInfo);
		}

		final Long supplierId = purchaseProduct.getSupplierId();
		BigDecimal purchasePrice = resource.getPurchasePrice(productId, supplierId);
		if (null != purchasePrice) {
			purchaseProduct.setCostPrice(purchasePrice);
		} else if (null != productId && null != supplierId) {
			failInfo.append("未设置该供应商的商品采购价;\n");
		}

		// 错误信息
		boolean isFail = failInfo.length() > 0;
		String resultInfo = isFail ? failInfo.substring(0, failInfo.length() - 1) : "验证通过";
		importDto.setFail(isFail);
		importDto.setResultInfo(resultInfo);
		return purchaseProduct;
	}


	/**
	 * 保存采购入库单数据
	 *
	 * @param purchaseProducts
	 */
	private void savePurchaseProduct(List<PurchaseProduct> purchaseProducts) {

		// （采购入库）商品需要按仓库，供应商分组
		// 目的是让相同供应商，仓库的商品数据放入同一个采购单内
		Map<String, List<PurchaseProduct>> group = purchaseProducts.stream().collect(
				Collectors.groupingBy(dto -> dto.getWarehouseId() + "--" + dto.getSupplierId())
		);

		// 按仓库，供应商分组,保存采购入库单;
		// 同时保存后的采购单id，编号回写到商品中。
		group.forEach((groupKey, products) -> {
			initAndSavePurchase(products);
		});

		// 保存（采购入库）商品
		iPurchaseProductService.saveBatch(purchaseProducts);

		// 商品入库时，会产生对应的库存流水
		iRepertoryFlowService.saveRepertoryFlowList(purchaseProducts, 1, 1);
	}

	/**
	 * 构建和保存采购入库
	 *
	 * @param products
	 * @return
	 */
	private Purchase initAndSavePurchase(List<PurchaseProduct> products) {

		Purchase purchase = new Purchase();
		purchase.setPurchaseNumber(RedisCode.getCode("RK"));
		purchase.setStatus(2);
		purchase.setStorageType(1);

		// products本身是供应商+仓库分组后的结果，所以获取第一个元素，填充相关字段值即可。
		PurchaseProduct purchaseProduct = products.get(0);
		purchase.setSupplierId(purchaseProduct.getSupplierId());
		purchase.setSupplierName(purchaseProduct.getSupplierName());
		purchase.setWarehouseId(purchaseProduct.getWarehouseId());
		purchase.setWarehouseName(purchaseProduct.getWarehouseName());
		purchase.setCreateTime(purchaseProduct.getCreateTime());
		purchase.setCreateBy(purchaseProduct.getCreateBy());
		purchase.setDeptId(purchaseProduct.getDeptId());
		purchase.setTenantId(purchaseProduct.getTenantId());
		purchase.setImportBatchNo(purchaseProduct.getImportBatchNo());
		// 保存道数据库中，获取id
		this.purchaseService.save(purchase);

		// 回填id和单号到商品数据中
		products.forEach(product -> {
			product.setPurchaseId(purchase.getId());
			product.setBusinessId(purchase.getId());// 设置这个值是因为，后续调用公共新增库存流水记录时要用到的
			product.setPurchaseNumber(purchase.getPurchaseNumber());// 设置这个值是因为，后续调用公共新增库存流水记录时要用到的
		});
		return purchase;
	}

	/**
	 * 获取数据库中与采购导入关联的数据，并且转换成以名称为key的map
	 *
	 * @return 关联的数据
	 */
	private PurchaseResourceDto getResoure() {

		// 仓库
		LambdaQueryWrapper<Warehouse> queryWarehouse = new LambdaQueryWrapper<Warehouse>()
				.select(Warehouse::getId, Warehouse::getWarehouseName, Warehouse::getDeptId)
				.eq(Warehouse::getStatus, 0)
				.eq(Warehouse::getDelFlag, 0);
		List<Warehouse> warehouseList = iWarehouseService.list(queryWarehouse);
		// 转map
		Map<String, Warehouse> warehouseMap =
				warehouseList.stream().collect(Collectors.groupingBy(Warehouse::getWarehouseName))
						.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
						e -> e.getValue().stream().findFirst().orElse(null)));

		// 供应商
		LambdaQueryWrapper<Supplier> querySupplier = new LambdaQueryWrapper<Supplier>()
				.select(Supplier::getId, Supplier::getUnitName)
				.eq(Supplier::getStatus, 0)
				.eq(Supplier::getDelFlag, 0);
		List<Supplier> supplierList = iSupplierService.list(querySupplier);
		// 转map
		Map<String, Supplier> supplierMap =
				supplierList.stream().collect(Collectors.groupingBy(Supplier::getUnitName))
						.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
						e -> e.getValue().stream().findFirst().orElse(null)));

		// 商品数据
		Product product = new Product();
		product.setStatus("0");
		List<ProductDto> productList = iProductService.selectProductList(product);
		// 转map
		Map<String, ProductDto> productMap =
				productList.stream().collect(Collectors.groupingBy(ProductDto::getProductName))
						.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
						e -> e.getValue().stream().findFirst().orElse(null)));

		// 供应商
		LambdaQueryWrapper<PurchasePrice> querypurchasePrice = new LambdaQueryWrapper<PurchasePrice>()
				.select(PurchasePrice::getSupplierId, PurchasePrice::getProductId, PurchasePrice::getPurchasePrice);
		List<PurchasePrice> purchasePriceList = purchasePriceService.list(querypurchasePrice);

		//组合数据，以商品+供应商id作为key，价格作为val
		Map<String, BigDecimal> purchasePriceMap =
				purchasePriceList.stream().collect(Collectors.groupingBy(a -> a.getProductId() + "--" + a.getSupplierId()))
						.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> {
					Optional<PurchasePrice> first = e.getValue().stream().findFirst();
					if (first.isPresent()) {
						return first.get().getPurchasePrice();
					}
					return BigDecimal.ZERO;
				}));

		PurchaseResourceDto resource = new PurchaseResourceDto();
		resource.setWarehouseMap(warehouseMap);
		resource.setSupplierMap(supplierMap);
		resource.setProductMap(productMap);
		resource.setPurchasePriceMap(purchasePriceMap);
		return resource;
	}

	/**
	 * 根据商品id获取商品参数组
	 *
	 * @param resource
	 * @param productId
	 * @return
	 */
	private List<AttributeParamDto> getProductParams(PurchaseResourceDto resource, Long productId) {

		// 先从内存中获取，如果获取不到再调用通用方法去查
		List<AttributeParamDto> paramList = resource.getProductParam(productId);
		if (null != paramList) {
			return paramList;
		}
		// 只有map为null才会去数据库查
		paramList = productService.getProductAttributeParamList(productId);

		if (null == paramList) {
			// 如果数据库中查到的是null，直接new一个空的放入resource中，减少后续同商品id反复查数据库。
			paramList = new ArrayList<>();
		}
		// 放入resource中
		resource.getProductParamMap().put(productId, paramList);
		// 返回
		return paramList;
	}


}
